package com.yami.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.dto.OrderRefundDto;
import com.yami.shop.bean.enums.StockBillStatus;
import com.yami.shop.bean.enums.StockBillType;
import com.yami.shop.bean.enums.StockType;
import com.yami.shop.bean.model.*;
import com.yami.shop.bean.param.ProductParam;
import com.yami.shop.bean.param.StockBillExportParam;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.i18n.LanguageEnum;
import com.yami.shop.common.util.Arith;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.common.util.PoiExcelUtil;
import com.yami.shop.dao.StockBillLogItemMapper;
import com.yami.shop.dao.StockBillLogMapper;
import com.yami.shop.manager.impl.LangManager;
import com.yami.shop.service.ProductService;
import com.yami.shop.service.SkuService;
import com.yami.shop.service.StockBillLogService;
import com.yami.shop.service.SupplierService;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 出入库明细
 *
 * @author LGH
 * @date 2021-09-09 13:11:15
 */
@Service
public class StockBillLogServiceImpl extends ServiceImpl<StockBillLogMapper, StockBillLog> implements StockBillLogService {
    @Autowired
    private StockBillLogMapper stockBillLogMapper;
    @Autowired
    private StockBillLogItemMapper stockBillLogItemMapper;
    @Autowired
    private ProductService productService;
    @Autowired
    private SkuService skuService;
    @Autowired
    private SupplierService supplierService;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private LangManager langManager;
    @Value("${yami.expose.operation.auth:}")
    private Boolean permission;

    /**
     * 导出出库明细excel列（中文）
     */
    private final static String[] OUT_EXPORT_COL_CN = {
            "出库单号", "关联订单号", "单据类型", "原因", "单据状态", "出库时间", "制单人", "备注", "制单时间",
            "商品名称", "商品编码", "规格", "出库数量", "成本单价", "成本金额"
    };
    /**
     * 导出入库明细excel列（中文）
     */
    private final static String[] IN_EXPORT_COL_CN = {
            "入库单号", "关联订单号", "单据类型", "原因", "单据状态", "入库时间", "制单人", "备注", "制单时间",
            "商品名称", "商品编码", "规格", "入库数量", "成本单价", "成本金额"
    };
    /**
     * 导出出库明细excel列（英文）
     */
    private final static String[] OUT_EXPORT_COL_EN = {
            "Outbound Order Number", "Related Order Number", "Document Type", "Reason", "Document Status", "Outbound Time", "Order Maker", "Remarks", "Order Time",
            "Product Name", "Commodity Code", "Specifications", "Quantity Out of Stock", "Unit Cost Price", "Cost Amount"
    };
    /**
     * 导出入库明细excel列（英文）
     */
    private final static String[] IN_EXPORT_COL_EN = {
            "Warehouse order number", "Related order number", "Document type", "Reason", "Document status", "Warehouse time", "Document maker", "Remarks", "Order time",
            "Product name", "Commodity Code", "Specifications", "Warehouse Quantity", "Cost Unit Price", "Cost Amount"
    };

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveInfo(StockBillLog stockBillLog) {
        Date now = new Date();
        // 校验部分信息
        this.checkInfo(stockBillLog);

        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);

        stockBillLog.setStockBillNo(this.getStockBillNoByType(null, StockType.instance(stockBillLog.getType())));
        // 处理金额并对商品完成出入库操作
        this.processingAmountAndStock(stockBillLog);
        // 保存出入货明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存出入货商品信息项
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLog.getStockBillLogItems());
    }

    @Override
    public IPage<StockBillLog> pageByParams(PageParam<StockBillLog> page, StockBillLog stockBillLog) {
        IPage<StockBillLog> stockBillLogIPage = stockBillLogMapper.pageByParams(page, stockBillLog);
        langManager.getStockBillLogLang(stockBillLogIPage.getRecords());
        return stockBillLogIPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfo(StockBillLog stockBillLog) {
        Date now = new Date();
        this.checkInfo(stockBillLog);
        stockBillLog.setUpdateTime(now);
        this.processingAmountAndStock(stockBillLog);

        // 更新出入库明细信息
        stockBillLogMapper.updateById(stockBillLog);
        // 更新出入货商品信息项
        stockBillLogItemMapper.delete(Wrappers.lambdaQuery(StockBillLogItem.class).eq(StockBillLogItem::getStockBillLogId, stockBillLog.getStockBillLogId()));
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLog.getStockBillLogItems());
    }

    @Override
    public void exportStockBillLog(StockBillLog stockBillLog, HttpServletResponse response) {
        List<StockBillExportParam> stockBillExportParamList = stockBillLogMapper.listExportStockBillLog(stockBillLog);
        langManager.getStockBillExportParamLang(stockBillExportParamList);
        for(StockBillExportParam stockBillExportParam : stockBillExportParamList){
            if (BooleanUtil.isFalse(permission) && StrUtil.isNotBlank(stockBillExportParam.getEmployeeMobile())) {
                stockBillExportParam.setEmployeeMobile(PhoneUtil.hideBetween(stockBillExportParam.getEmployeeMobile()).toString());
            }
        }
        if (CollUtil.isEmpty(stockBillExportParamList)) {
            return;
        }
        ExcelWriter writer = ExcelUtil.getBigWriter();
        final List<String> exportCol = Arrays.asList(
                Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ?
                        Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? OUT_EXPORT_COL_CN : IN_EXPORT_COL_CN
                        : Objects.equals(stockBillLog.getType(), StockType.WAREHOUSING.value()) ? OUT_EXPORT_COL_EN : IN_EXPORT_COL_EN
        );
        // 最大列数量
        int maxColNum = exportCol.size();

        Sheet sheet = writer.getSheet();
        // 设置列宽
        this.setColumnWidth(sheet);
        // 标题
        writer.merge(maxColNum - 1, Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ?
                Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? "出库信息整理" : "入库信息整理"
                : Objects.equals(stockBillLog.getType(), StockType.WAREHOUSING.value()) ? "Outgoing Information Arrangement" : "Incoming Information Arrangement"
        );
        // 列名
        writer.writeRow(exportCol);

        Map<String, List<StockBillExportParam>> stockBillMap = new LinkedHashMap<>();
        // 用map把出入库单号相同的项目合并
        for (StockBillExportParam stockBillExportParam : stockBillExportParamList) {
            List<StockBillExportParam> paramList = stockBillMap.getOrDefault(stockBillExportParam.getStockBillNo(), new ArrayList<StockBillExportParam>());
            paramList.add(stockBillExportParam);
            stockBillMap.put(stockBillExportParam.getStockBillNo(), paramList);
        }

        int row = 1;
        for (Map.Entry<String, List<StockBillExportParam>> entry : stockBillMap.entrySet()) {

            final String StockBillNo = entry.getKey();
            final List<StockBillExportParam> paramList = entry.getValue();
            // 取出任意一个stockBillExportParam（因为paramList中的实例都有一部分公共属性）
            final StockBillExportParam stockBillExportParam = paramList.get(0);
            int firstRow = row + 1;
            int lastRow = row + paramList.size();
            int col = -1;

            // 出入库单号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getStockBillNo());
            // 关联订单号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getSourceOrderNo());
            // 单据类型
            StockBillType stockBillType = StockBillType.instance(stockBillExportParam.getStockBillType());
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, Objects.isNull(stockBillType) ? "" : Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ? stockBillType.text() : stockBillType.textEn());

            // 原因
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getReason());
            // 状态
            StockBillStatus stockBillStatus = StockBillStatus.instance(stockBillExportParam.getStatus());
            String statusStr;
            if (Objects.isNull(stockBillStatus)) {
                statusStr = "";
            } else {
                if (Objects.equals(I18nMessage.getDbLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang())) {
                    statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                            (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? "已出库" : "已入库") :
                            stockBillStatus.text();
                } else {
                    statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                            (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? "Out of storage" : "Stocked") :
                            stockBillStatus.textEn();
                }
            }
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, statusStr);

            // 出入库时间
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getBusinessTime());
            // 制单人手机号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getEmployeeMobile());
            // 备注
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getRemark());
            // 创建时间
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, stockBillExportParam.getCreateTime());


            for (StockBillExportParam billExportParam : paramList) {
                row++;
                int partCol = col;
                // 商品名称
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getProdName());
                // 商品编码
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getPartyCode());
                // sku名称
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getSkuName());
                // 实际出入库数量
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getStockCount());
                // 成本单价
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, stockBillExportParam.getUnitPrice());
                // 成本金额
                PoiExcelUtil.mergeIfNeed(writer, row, row, ++partCol, partCol, Arith.mul(stockBillExportParam.getStockCount(), stockBillExportParam.getUnitPrice()));
            }

        }

//        writeMethod2(stockBillLog, stockBillExportParamList, writer, rowIndex);
        PoiExcelUtil.writeExcel(response, writer);
    }


    /**
     * @author chiley
     * @create 2022/5/17 17:34
     */

    private void writeMethod2(StockBillLog stockBillLog, List<StockBillExportParam> stockBillExportParamList, ExcelWriter writer, int rowIndex) {
        int i;
        int j = 0;
        int col = 0;
        for (i = 0; i < stockBillExportParamList.size() - 1; i++, rowIndex++) {
            col = -1;

            j = i;
            while (j < stockBillExportParamList.size() - 1 && Objects.equals(stockBillExportParamList.get(j).getStockBillNo(), stockBillExportParamList.get(j + 1).getStockBillNo())) {
                j++;
                rowIndex++;
            }
            // 出入库单号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getStockBillNo());
            // 关联订单号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getSourceOrderNo());
            // 单据类型
            StockBillType stockBillType = StockBillType.instance(stockBillExportParamList.get(i).getStockBillType());
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, Objects.isNull(stockBillType) ? "" : Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ? stockBillType.text() : stockBillType.textEn());
            // 原因
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getReason());

            // 状态
            StockBillStatus stockBillStatus = StockBillStatus.instance(stockBillExportParamList.get(i).getStatus());
            String statusStr;
            if (Objects.isNull(stockBillStatus)) {
                statusStr = "";
            } else {
                if (Objects.equals(I18nMessage.getDbLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang())) {
                    statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                            (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? "已出库" : "已入库") :
                            stockBillStatus.text();
                } else {
                    statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                            (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? "Out of storage" : "Stocked") :
                            stockBillStatus.textEn();
                }
            }
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, statusStr);

            // 出入库时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getBusinessTime());
            // 制单人手机号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getEmployeeMobile());

            // 备注
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getRemark());
            // 创建时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex - (j - i), rowIndex, ++col, col, stockBillExportParamList.get(i).getCreateTime());

            i = j;

        }

        // 检查最后一行有没有写
        if (j < stockBillExportParamList.size() - 1) {

            int acol = -1;
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getStockBillNo());
            // 关联订单号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getSourceOrderNo());
            // 单据类型
            StockBillType stockBillType = StockBillType.instance(stockBillExportParamList.get(i).getStockBillType());
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, Objects.isNull(stockBillType) ? "" : Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ? stockBillType.text() : stockBillType.textEn());
            // 原因
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getReason());
            // 状态
            StockBillStatus stockBillStatus = StockBillStatus.instance(stockBillExportParamList.get(i).getStatus());
            String statusStr;
            if (Objects.isNull(stockBillStatus)) {
                statusStr = "";
            } else {
                if (Objects.equals(I18nMessage.getDbLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang())) {
                    statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                            (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? "已出库" : "已入库") :
                            stockBillStatus.text();
                } else {
                    statusStr = Objects.equals(stockBillStatus.value(), StockBillStatus.SUCCESS.value()) ?
                            (Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? "Out of storage" : "Stocked") :
                            stockBillStatus.textEn();
                }
            }
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, statusStr);
            // 出入库时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getBusinessTime());
            // 制单人手机号
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getEmployeeMobile());
            // 备注
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getRemark());
            // 创建时间
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++acol, acol, stockBillExportParamList.get(i).getCreateTime());
        }

        // 逐行写入不重复的内容
        rowIndex = 2;
        int temp = col;
        for (StockBillExportParam stockBillExportParam : stockBillExportParamList) {
            col = temp;
            // 商品名称
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getProdName());
            // 商品编码
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getPartyCode());
            // sku名称
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getSkuName());
            // 实际出入库数量
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getStockCount());
            // 成本单价
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, stockBillExportParam.getUnitPrice());
            // 成本金额
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, Arith.mul(stockBillExportParam.getStockCount(), stockBillExportParam.getUnitPrice()));
            ++rowIndex;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initialize(StockBillLog stockBillLog, List<Sku> skuList) {
        if (CollUtil.isEmpty(skuList)) {
            return;
        }
        Date now = new Date();
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setStockBillType(StockBillType.INITIALIZE.value());
        stockBillLog.setType(StockType.WAREHOUSING.value());
        stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
        stockBillLog.setStockBillNo(this.getStockBillNoByType(null, StockType.instance(StockType.WAREHOUSING.value())));
        stockBillLog.setBusinessTime(now);

        List<StockBillLogItem> stockBillLogItemList = new ArrayList<>();
        double totalAmount = 0.0;
        Integer totalCount = 0;
        if (CollUtil.isNotEmpty(skuList)) {
            // 商品
            for (Sku sku : skuList) {
                StockBillLogItem stockBillLogItem = this.getStockBillLogItem(
                        sku.getSkuId(),
                        sku.getProdId(),
                        sku.getPrice(),
                        sku.getStocks(),
                        sku.getStocks()
                );
                totalCount += sku.getStocks();
                totalAmount += Arith.mul(sku.getStocks(), sku.getPrice());
                stockBillLogItemList.add(stockBillLogItem);
            }
        }
        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setTotalAmount(totalAmount);
        // 保存初始化明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存初始化商品项信息
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLogItemList);
    }

    @Override
    public IPage<StockBillLogItem> inquire(PageParam<StockBillLog> page, ProductParam productParam) {
        return skuService.inquireStock(page, productParam);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateStockLogBySkuInfo(String orderNumber, Long prodId, Long skuId, Integer changeStocks, Integer stockBillType, Integer stockType) {
        Date now = new Date();
        Product product = productService.getOne(Wrappers.lambdaQuery(Product.class).eq(Product::getProdId, prodId));
//        if (!Objects.equals(product.getIsCompose(), 1)) {
//            return;
//        }
        Sku sku = skuService.getOne(Wrappers.lambdaQuery(Sku.class).eq(Sku::getSkuId, skuId));

        StockBillLog stockBillLog = this.getStockBillLog(
                product.getShopId(),
                null,
                now,
                stockBillType,
                stockType,
                this.getStockBillNoByType(null, StockType.OUT_OF_STOCK),
                StockBillStatus.SUCCESS.value(),
                orderNumber
        );
        stockBillLog.setTotalCount(changeStocks);
        stockBillLog.setTotalAmount(Arith.mul(changeStocks, sku.getPrice()));
        // 保存初始化明细信息
        stockBillLogMapper.insert(stockBillLog);

        StockBillLogItem stockBillLogItem = this.getStockBillLogItem(
                skuId,
                prodId,
                sku.getPrice(),
                changeStocks,
                sku.getStocks()
        );
        stockBillLogItem.setStockBillLogId(stockBillLog.getStockBillLogId());
        // 保存初始化商品项信息
        stockBillLogItemMapper.insert(stockBillLogItem);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateStockLogOfOrderList(List<Order> orders, Integer stockBillType, Integer stockType) {
        if (CollUtil.isEmpty(orders)) {
            return;
        }
        Date now = new Date();
        for (Order order : orders) {
            StockBillLog stockBillLog = this.getStockBillLog(
                    order.getShopId(),
                    null,
                    now,
                    stockBillType,
                    stockType,
                    this.getStockBillNoByType(null, StockType.instance(stockType)),
                    StockBillStatus.SUCCESS.value(),
                    order.getOrderNumber()
            );
            this.generateStockLogOfOrderItemList(order.getOrderItems(), stockBillLog, false);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateStockLogOfRefundOrder(OrderRefundDto orderRefundDto, Integer stockBillType, Integer stockType) {
        Date now = new Date();
        StockBillLog stockBillLog = this.getStockBillLog(
                orderRefundDto.getShopId(),
                null,
                now,
                stockBillType,
                stockType,
                this.getStockBillNoByType(null, StockType.instance(stockType)),
                StockBillStatus.SUCCESS.value(),
                orderRefundDto.getOrderNumber()
        );
        this.generateStockLogOfOrderItemList(orderRefundDto.getOrderItems(), stockBillLog, true);
    }

    private StockBillLog getStockBillLog(Long shopId, Long employeeId, Date businessTime, Integer stockBillLogType, Integer stockType, String stockBillNo, Integer status, String sourceOrderNo) {
        Date now = new Date();
        StockBillLog stockBillLog = new StockBillLog();
        stockBillLog.setShopId(shopId);
        stockBillLog.setEmployeeId(employeeId);
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setBusinessTime(businessTime);
        stockBillLog.setStockBillType(stockBillLogType);
        stockBillLog.setType(stockType);
        stockBillLog.setStockBillNo(stockBillNo);
        stockBillLog.setStatus(status);
        stockBillLog.setSourceOrderNo(sourceOrderNo);
        return stockBillLog;
    }

    private StockBillLogItem getStockBillLogItem(Long skuId, Long prodId, Double unitPrice, Integer stockCount, Integer afterStock) {
        StockBillLogItem stockBillLogItem = new StockBillLogItem();
        stockBillLogItem.setSkuId(skuId);
        stockBillLogItem.setProdId(prodId);
        stockBillLogItem.setUnitPrice(unitPrice);
        stockBillLogItem.setStockCount(stockCount);
        stockBillLogItem.setAfterStock(afterStock);
        return stockBillLogItem;
    }

    /**
     * 根据订单项完成库存明细信息的生成和保存
     *
     * @param orderItems
     * @param stockBillLog
     * @param isRefundOrder
     */
    private void generateStockLogOfOrderItemList(List<OrderItem> orderItems, StockBillLog stockBillLog, Boolean isRefundOrder) {
        List<StockBillLogItem> stockBillLogItemList = new ArrayList<>();
        double totalAmount = 0.0;
        Integer totalCount = 0;
        Map<Long, Product> productMap = new HashMap<>(16);
        for (OrderItem orderItem : orderItems) {
            if (isRefundOrder && orderItem.getStatus() == 0) {
                continue;
            }
            Product product;
            if (productMap.containsKey(orderItem.getProdId())) {
                product = productMap.get(orderItem.getProdId());
            } else {
                product = productService.getOne(Wrappers.lambdaQuery(Product.class).eq(Product::getProdId, orderItem.getProdId()));
                productMap.put(product.getProdId(), product);
            }
            // 普通商品sku
            Sku sku = skuService.getOne(Wrappers.lambdaQuery(Sku.class).eq(Sku::getSkuId, orderItem.getSkuId()));
            // 删除的sku不再添加记录
            if (Objects.equals(sku.getIsDelete(), 1)) {
                continue;
            }
            StockBillLogItem stockBillLogItem = this.getStockBillLogItem(
                    orderItem.getSkuId(),
                    orderItem.getProdId(),
                    sku.getPrice(),
                    isRefundOrder ? (orderItem.getStatus() == -1 ? orderItem.getProdCount() : orderItem.getStatus()) : orderItem.getProdCount(),
                    sku.getStocks()
            );
            totalAmount = Arith.add(totalAmount, Arith.mul(isRefundOrder ? (orderItem.getStatus() == -1 ? orderItem.getProdCount() : orderItem.getStatus()) : orderItem.getProdCount(), sku.getPrice()));
            totalCount += isRefundOrder ? (orderItem.getStatus() == -1 ? orderItem.getProdCount() : orderItem.getStatus()) : orderItem.getProdCount();
            stockBillLogItemList.add(stockBillLogItem);
        }
        if (totalCount == 0) {
            return;
        }
        stockBillLog.setTotalAmount(totalAmount);
        stockBillLog.setTotalCount(totalCount);
        // 保存初始化明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存初始化商品项信息
        if (CollUtil.isEmpty(stockBillLogItemList)) {
            return;
        }
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLogItemList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void voided(Long shopId, Long stockBillLogId) {
        StockBillLog stockBillLog = stockBillLogMapper.selectById(stockBillLogId);
        if (Objects.isNull(stockBillLog) || !Objects.equals(stockBillLog.getStatus(), StockBillStatus.AWAIT_SUBMIT.value())) {
            throw new YamiShopBindException("当前数据不存在或状态已经发生改变，请刷新后重试");
        }
        if (!Objects.equals(shopId, stockBillLog.getShopId()) && !Objects.equals(shopId, Constant.PLATFORM_SHOP_ID)) {
            throw new YamiShopBindException("当前数据不属于你的店铺");
        }
        boolean isSuccess = this.update(Wrappers.lambdaUpdate(StockBillLog.class)
                .set(StockBillLog::getStatus, StockBillStatus.ABOLISHED.value())
                .eq(StockBillLog::getStockBillLogId, stockBillLogId)
                .eq(StockBillLog::getStatus, stockBillLog.getStatus())
        );
        if (!isSuccess) {
            throw new YamiShopBindException("操作失败，请刷新后重试");
        }
    }

    @Override
    public StockBillLog getByStockBillLogId(Long stockBillLogId) {
        StockBillLog stockBillLog = stockBillLogMapper.getByStockBillLogId(stockBillLogId);
        langManager.getStockBillLogLang(stockBillLog);
        return stockBillLog;
    }

    @Override
    public IPage<StockBillLog> purchasePage(PageParam<StockBillLog> page, StockBillLog stockBillLog) {
        IPage<StockBillLog> stockBillLogPage = stockBillLogMapper.pageByParams(page, stockBillLog);
        langManager.getStockBillLogLang(stockBillLogPage.getRecords());
        return stockBillLogPage;
    }

    @Override
    public StockBillLog purchaseInfo(Long stockBillLogId) {
        StockBillLog stockBillLog = stockBillLogMapper.info(stockBillLogId);
        if (Objects.isNull(stockBillLog)) {
            throw new YamiShopBindException("yami.data.deleted.or.not.exist");
        }
        if (CollUtil.isNotEmpty(stockBillLog.getStockBillLogItems())) {
            loadProdAndSkuInfo(stockBillLog.getStockBillLogItems());
        }
        return stockBillLog;
    }

    @Override
    public void generateStockLogOfEditInfo(StockBillLog stockBillLog, List<Sku> skuList) {
        Date now = new Date();
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setBusinessTime(now);
        stockBillLog.setStockBillNo((Objects.equals(stockBillLog.getStockBillType(), StockBillType.EDIT_OUTBOUND.value()) ? "OB" : "IB") + snowflake.nextId());
        stockBillLog.setType(Objects.equals(stockBillLog.getStockBillType(), StockBillType.EDIT_OUTBOUND.value()) ? StockType.OUT_OF_STOCK.value() : StockType.WAREHOUSING.value());
        stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
        List<StockBillLogItem> stockBillLogItemList = new ArrayList<>();
        double totalAmount = 0.0;
        int totalCount = 0;
        if (CollUtil.isNotEmpty(skuList)) {
            for (Sku sku : skuList) {
                int changeStock = sku.getChangeStock() < 0 ? -sku.getChangeStock() : sku.getChangeStock();
                StockBillLogItem stockBillLogItem = this.getStockBillLogItem(
                        sku.getSkuId(),
                        sku.getProdId(),
                        sku.getPrice(),
                        changeStock,
                        sku.getStocks()
                );
                totalCount += changeStock;
                totalAmount = Arith.add(totalAmount, Arith.mul(sku.getPrice(), changeStock));
                stockBillLogItemList.add(stockBillLogItem);
            }
        }
        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setTotalAmount(totalAmount);
        // 保存初始化明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存初始化商品项信息
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLogItemList);
    }

    @Override
    public void generateStockLogOfTakeStock(StockBillLog stockBillLog, List<Sku> skuList) {
        if (CollUtil.isEmpty(skuList)) {
            return;
        }
        Date now = new Date();
        stockBillLog.setCreateTime(now);
        stockBillLog.setUpdateTime(now);
        stockBillLog.setBusinessTime(now);
        stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
        stockBillLog.setStockBillNo(this.getStockBillNoByType(StockBillType.instance(stockBillLog.getStockBillType()), null));
        stockBillLog.setType(Objects.equals(stockBillLog.getStockBillType(), StockBillType.LOSS_OUTBOUND.value()) ? StockType.OUT_OF_STOCK.value() : StockType.WAREHOUSING.value());
        List<StockBillLogItem> stockBillLogItemList = new ArrayList<>();
        double totalAmount = 0.0;
        int totalCount = 0;
        if (CollUtil.isNotEmpty(skuList)) {
            for (Sku sku : skuList) {
                int changeStock = sku.getChangeStock() < 0 ? -sku.getChangeStock() : sku.getChangeStock();
                StockBillLogItem stockBillLogItem = this.getStockBillLogItem(
                        sku.getSkuId(),
                        sku.getProdId(),
                        sku.getPrice(),
                        changeStock,
                        sku.getStocks()
                );
                totalCount += changeStock;
                totalAmount = Arith.add(totalAmount, Arith.mul(sku.getPrice(), changeStock));
                stockBillLogItemList.add(stockBillLogItem);
            }
        }

        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setTotalAmount(totalAmount);
        // 保存初始化明细信息
        stockBillLogMapper.insert(stockBillLog);
        // 保存初始化商品项信息
        stockBillLogItemMapper.insertBatch(stockBillLog.getStockBillLogId(), stockBillLogItemList);
    }

    @Override
    public void removeEmptyLog() {
        stockBillLogMapper.removeEmptyLog();
    }

    private void loadProdAndSkuInfo(List<StockBillLogItem> stockBillLogItems) {
        List<Long> prodIds = new ArrayList<>();
        List<Long> skuIds = new ArrayList<>();
        for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
            prodIds.add(stockBillLogItem.getProdId());
            skuIds.add(stockBillLogItem.getSkuId());
        }
        // 普通商品信息
        Map<Long, Product> prodMap;
        if (CollUtil.isNotEmpty(prodIds) && CollUtil.isNotEmpty(skuIds)) {
            List<Product> products = productService.prodAndSkuInfo(prodIds, skuIds);
            prodMap = products.stream().collect(Collectors.toMap(Product::getProdId, p -> p));
        } else {
            prodMap = new HashMap<>(0);
        }
        for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
            if (!prodMap.containsKey(stockBillLogItem.getProdId())) {
                continue;
            }
            Product product = prodMap.get(stockBillLogItem.getProdId());
            Sku sku = null;
            for (Sku skuDb : product.getSkuList()) {
                if (Objects.equals(skuDb.getSkuId(), stockBillLogItem.getSkuId())) {
                    sku = skuDb;
                    break;
                }
            }
            if (Objects.isNull(sku)) {
                return;
            }
            stockBillLogItem.setPic(product.getPic());
            stockBillLogItem.setProdName(product.getProdName());
            stockBillLogItem.setSkuName(sku.getSkuName());
            stockBillLogItem.setPartyCode(sku.getPartyCode());
            stockBillLogItem.setIsDelete(sku.getIsDelete());
            stockBillLogItem.setStocks(sku.getStocks());
        }
    }

    private void setColumnWidth(Sheet sheet) {
        // 出入库单号
        sheet.setColumnWidth(0, 40 * 256);
        // 关联订单号
        sheet.setColumnWidth(1, 40 * 256);
        // 单据类型
        sheet.setColumnWidth(2, 20 * 256);
        // 状态
        sheet.setColumnWidth(3, 20 * 256);
        // 出入库时间
        sheet.setColumnWidth(4, 20 * 256);
        // 制单人手机号
        sheet.setColumnWidth(5, 20 * 256);
        // 备注
        sheet.setColumnWidth(6, 60 * 256);
        // 创建时间
        sheet.setColumnWidth(7, 20 * 256);
        // 商品名称
        sheet.setColumnWidth(8, 40 * 256);
        // 商品编码
        sheet.setColumnWidth(9, 40 * 256);
        // sku名称
        sheet.setColumnWidth(10, 30 * 256);
        // 实际出入库数量
        sheet.setColumnWidth(11, 20 * 256);
        // 成本单价
        sheet.setColumnWidth(12, 20 * 256);
        // 成本金额
        sheet.setColumnWidth(13, 20 * 256);
        // 商品备注
        sheet.setColumnWidth(14, 60 * 256);
    }

    /**
     * 校验出入库明细信息
     *
     * @param stockBillLog
     */
    private void checkInfo(StockBillLog stockBillLog) {
        if (CollUtil.isEmpty(stockBillLog.getStockBillLogItems())) {
            // 出入库商品项不能为空
            throw new YamiShopBindException("商品项不能为空");
        }
        if (Objects.nonNull(stockBillLog.getStockBillLogId())) {
            // 更新时校验
            StockBillLog dbStockBillLog = stockBillLogMapper.selectById(stockBillLog.getStockBillLogId());
            if (!Objects.equals(stockBillLog.getShopId(), dbStockBillLog.getShopId())) {
                throw new YamiShopBindException("当前出入库信息不属于你的店铺");
            }
            if (!Objects.equals(dbStockBillLog.getStatus(), StockBillStatus.AWAIT_SUBMIT.value())) {
                throw new YamiShopBindException("当前信息已完成或已作废，无法继续修改，请刷新页面");
            }
            stockBillLog.setStockBillNo(dbStockBillLog.getStockBillNo());
        }
    }

    private void processingAmountAndStock(StockBillLog stockBillLog) {
        List<StockBillLogItem> stockBillLogItems = stockBillLog.getStockBillLogItems();
        Sku sku = null;
        boolean flag = false;
        Integer totalCount = 0;
        double totalAmount = 0.0;
        if (Objects.equals(stockBillLog.getStockBillType(), StockBillType.PURCHASE_STORAGE.value())) {
            // 采购入库操作
            stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
            for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
                // 更新商品与sku的库存
                this.updateProdStock(stockBillLogItem.getProdId(), stockBillLogItem.getSkuId(), Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? -stockBillLogItem.getStockCount() : stockBillLogItem.getStockCount());
                sku = skuService.getOne(Wrappers.lambdaQuery(Sku.class)
                        .eq(Sku::getSkuId, stockBillLogItem.getSkuId())
                );
                stockBillLogItem.setAfterStock(sku.getStocks());
            }
        } else {
            // 其他操作
            for (StockBillLogItem stockBillLogItem : stockBillLogItems) {
                // 普通商品出入库操作
                if (Objects.equals(stockBillLog.getStatus(), StockBillStatus.SUCCESS.value())) {
                    // 更新商品与sku的库存
                    this.updateProdStock(stockBillLogItem.getProdId(), stockBillLogItem.getSkuId(), Objects.equals(stockBillLog.getType(), StockType.OUT_OF_STOCK.value()) ? -stockBillLogItem.getStockCount() : stockBillLogItem.getStockCount());
                }
                sku = skuService.getOne(Wrappers.lambdaQuery(Sku.class)
                        .eq(Sku::getSkuId, stockBillLogItem.getSkuId())
                );
                stockBillLogItem.setAfterStock(sku.getStocks());
                // 设置默认单价
                stockBillLogItem.setUnitPrice(sku.getPrice());
                // 累计总金额
                totalAmount = Arith.add(totalAmount, Arith.mul(stockBillLogItem.getStockCount(), sku.getPrice()));
                // 累计总数量
                totalCount += stockBillLogItem.getStockCount();
            }
            stockBillLog.setTotalCount(totalCount);
            stockBillLog.setTotalAmount(totalAmount);
        }
    }

    /**
     * 根据类型生成出入库单号
     *
     * @param stockBillType
     * @return
     */
    private String getStockBillNoByType(StockBillType stockBillType, StockType stockType) {
        if (Objects.nonNull(stockBillType)) {
            switch (stockBillType) {
                case PURCHASE_STORAGE:
                case RETURN_STORAGE:
                case OTHER_STORAGE:
                case INITIALIZE:
                case ORDER_CANCEL:
                case EDIT_STORAGE:
                case PROFIT_STORAGE:
                    // 入库单号
                    return "IB" + snowflake.nextId();
                case PURCHASE_OUTBOUND:
                case EDIT_OUTBOUND:
                case LOSS_OUTBOUND:
                case OTHER_OUTBOUND:
                    // 出库单号
                    return "OB" + snowflake.nextId();
                default:
                    break;
            }
        }
        if (Objects.nonNull(stockType)) {
            switch (stockType) {
                case OUT_OF_STOCK:
                    // 出库单号
                    return "OB" + snowflake.nextId();
                case WAREHOUSING:
                    // 入库单号
                    return "IB" + snowflake.nextId();
                default:
                    break;
            }
        }
        return null;
    }

    /**
     * 更新商品库存
     *
     * @param prodId
     * @param skuId
     * @param changeCount
     */
    private void updateProdStock(Long prodId, Long skuId, Integer changeCount) {
        // 更新商品与sku的库存
        boolean flag = productService.updateProdAndSkuStock(prodId, skuId, changeCount);
        if (!flag) {
            throw new YamiShopBindException("yami.shop.stock.update.error");
        }
        productService.removeProdCacheByProdId(prodId);
        skuService.removeSkuCacheBySkuId(skuId, prodId);
    }

}
